﻿using System;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;

namespace Inovout.Descriptors
{
    public class TypeDescriptor : MemberDescriptor, ITypeDescriptor
    {
        #region Constructor

        /// <summary>
        /// 初始化 EnumDictionary类的新实例。
        /// </summary>
        /// <param name="enumType">枚举声明类型</param>
        public TypeDescriptor(Type classType)
            : base(classType)
        {
            ArgumentAssert.IsNotNull(classType, "classType");
            constructors = classType.GetConstructors()
                .Select(constructorInfo => new ConstructorDescriptor(constructorInfo)).ToArray();

            Type ownerType = classType;
            properties = new PropertyDescriptor[0];
            while (ownerType != null)
            {
                properties = properties.Union(ownerType.GetProperties(AllBindingFlags)
                     .Select(propertyInfo => new PropertyDescriptor(propertyInfo))).ToArray(); ;
                ownerType = ownerType.BaseType;
            }
            ownerType = classType;
            fields = new FieldDescriptor[0];
            while (ownerType != null)
            {
                fields = fields.Union(ownerType.GetFields(AllBindingFlags)
                      .Select(fieldInfo => new FieldDescriptor(fieldInfo))).ToArray();
                ownerType = ownerType.BaseType;
            }
        }


        #endregion

        private readonly FieldDescriptor[] fields;
        public FieldDescriptor[] Fields
        {
            get { return fields; }
        }

        public PropertyDescriptor GetProperty(string propertyName)
        {
            return properties.Single(property => property.Name.Equals(propertyName));
        }


        private readonly PropertyDescriptor[] properties;
        public PropertyDescriptor[] Properties
        {
            get { return properties; }
        }

        private readonly ConstructorDescriptor[] constructors;
        public ConstructorDescriptor[] Constructors
        {
            get { return constructors; }
        }
        public static TypeDescriptor Create<TClass>() where TClass : class
        {
            return Create(typeof(TClass));
        }
        public static TypeDescriptor Create(Type type)
        {
            return new TypeDescriptor(type);
        }

        public FieldDescriptor GetField(string fieldName)
        {
            return fields.SingleOrDefault(field => field.Name.Equals(fieldName));
        }

        public bool HasProperty(string propertyName)
        {
            return GetProperty(propertyName) != null;
        }

        public bool HasField(string fieldName)
        {
            return GetField(fieldName) != null;
        }

        public Dictionary<string, object> GetPropertyValues(object instance)
        {
            ArgumentAssert.IsNotNull(instance, "instance");
            return properties.ToDictionary(property => property.Name, property => property.GetValue(instance));
        }
        public void SetPropertyValues(object instance, IDictionary<string, object> propertyValues)
        {
            ArgumentAssert.IsNotNull(instance, "instance");
            ArgumentAssert.IsNotNull(propertyValues, "propertyValues");
            foreach (PropertyDescriptor propertyDesciptor in properties.Where(property => !property.IsReadOnly))
            {
                object value;
                if (propertyValues.TryGetValue(propertyDesciptor.Name, out value))
                {
                    propertyDesciptor.SetValue(instance, value);
                }
            }

        }

    }
}
